package cmd

import (
	"bytes"
	"crypto/md5"
	"crypto/rand"
	"crypto/sha1"
	"crypto/sha256"
	"fmt"
	"hash/crc32"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"
)

func FileExists(filePath string) bool {
	f, err := os.Stat(filePath)
	return err == nil && !f.IsDir()
}

func DirExists(filePath string) bool {
	d, err := os.Stat(filePath)
	return err == nil && d.IsDir()
}

// 文件元信息
type FileInfo struct {
	FileName      string
	DirPath       string
	AbsPath       string
	RelPath       string
	FileSize      int
	FileData      []byte
	LzssData      []byte
	ModTime       time.Time
	Key           string
	HashAlgorithm string
}

func (f *FileInfo) ReadFileData() []byte {
	if f.FileData == nil {
		fd, err := os.ReadFile(f.AbsPath)
		if err != nil {
			panic(err)
		}
		f.FileData = fd
	}
	return f.FileData
}

func CheckFileExists(filePath string) bool {
	_, err := os.Stat(filePath)
	return err == nil
}

func BytesMD5(data []byte) string {
	hash := md5.New()
	src := io.Reader(bytes.NewReader(data))
	if _, err := io.Copy(hash, src); err != nil {
		return ""
	}
	sum := hash.Sum(nil)
	return fmt.Sprintf("%X", sum)
}

// 计算文件的 MD5 值
func CalcMD5(filePath string) string {
	// 打开文件
	file, err := os.Open(filePath)
	if err != nil {
		return ""
	}
	defer file.Close()

	// 创建一个 md5 hash 写入器
	hash := md5.New()
	if _, err := io.Copy(hash, file); err != nil {
		return ""
	}

	// 获取 16 字节的哈希值
	sum := hash.Sum(nil)

	// 转换为 32 位十六进制字符串
	return fmt.Sprintf("%X", sum)
}

func FileMD5(filePath string) string {
	return CalcMD5(filePath)
}

func StringMD5(str string) string {
	hash := md5.New()
	hash.Write([]byte(str))
	sum := hash.Sum(nil)
	return fmt.Sprintf("%X", sum)
}

// 计算文件的 SHA1 值
func FileSHA1(filePath string) string {
	// 打开文件
	file, err := os.Open(filePath)
	if err != nil {
		return ""
	}
	defer file.Close()

	// 创建一个 sha1 hash 写入器
	hash := sha1.New()
	if _, err := io.Copy(hash, file); err != nil {
		return ""
	}

	// 获取 16 字节的哈希值
	sum := hash.Sum(nil)

	// 转换为 32 位十六进制字符串
	return fmt.Sprintf("%X", sum)
}

// 计算文件的 SHA256 值
func FileSHA256(filePath string) string {
	// 打开文件
	file, err := os.Open(filePath)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	// 创建一个 sha256 hash 写入器
	hash := sha256.New()
	if _, err := io.Copy(hash, file); err != nil {
		panic(err)
	}

	// 获取 16 字节的哈希值
	sum := hash.Sum(nil)

	// 转换为 32 位十六进制字符串
	return fmt.Sprintf("%X", sum)
}

// GetRootFiles 获取指定目录下根目录中的文件列表
// dirPath: 要遍历的目录路径
// relativePath: 如果非空，则返回相对于此路径的相对路径；否则返回绝对路径
// filterFileTypes: 要过滤的文件后缀名列表（如 ".txt", ".jpg"），为空或 nil 表示不过滤
func GetRootFiles(dirPath string, relativePath bool, filterFileTypes []string) []string {
	var fileList []string

	// 读取目录内容
	files, err := os.ReadDir(dirPath)
	if err != nil {
		log.Printf("⚠️ 无法读取目录 %s: %v", dirPath, err)
		return fileList
	}

	for _, file := range files {
		if file.IsDir() {
			continue // 忽略子目录
		}

		fileName := file.Name()
		filePath := filepath.Join(dirPath, fileName)

		// 文件类型过滤
		if len(filterFileTypes) > 0 {
			ext := strings.ToLower(filepath.Ext(fileName))
			match := false
			for _, ft := range filterFileTypes {
				if strings.HasPrefix(ft, ".") && ext == strings.ToLower(ft) {
					match = true
					break
				} else if "."+ext == strings.ToLower(ft) {
					match = true
					break
				}
			}
			if !match {
				continue
			}
		}

		// 是否转换为相对路径
		if relativePath {
			fileList = append(fileList, fileName)
		} else {
			fileList = append(fileList, filePath)
		}
	}

	return fileList
}

// GetFileList 根据输入的文件夹路径, 输出一个文件列表(该文件夹内所有层级的文件在系统的绝对完整路径,不包括文件夹)
func GetFileList(dirPath string, relativePath bool) []string {
	var fileList []string
	err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
		// 排除 dirPath
		if path == dirPath {
			return nil
		}
		// 排除临时文件
		if IsTmpFileOrNotExists(path) {
			return nil
		}

		if !info.IsDir() {
			if relativePath {
				rel, _ := GetRelativePath(dirPath, path)
				fileList = append(fileList, rel)
			} else {
				fileList = append(fileList, path)
			}
		}
		return nil
	})
	if err != nil {
		panic(err)
	}
	return fileList
}

// GetDirList 根据输入的文件夹路径, 输出一个文件夹列表(该文件夹内所有层级的文件夹在系统的绝对完整路径,不包括文件)
func GetDirList(dirPath string, relativePath bool) []string {
	var dirList []string
	err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
		// 排除 dirPath
		if path == dirPath {
			return nil
		}
		// 排除临时文件
		if IsTmpFileOrNotExists(path) {
			return nil
		}

		if info.IsDir() {
			if relativePath {
				rel, _ := GetRelativePath(dirPath, path)
				dirList = append(dirList, rel)
			} else {
				dirList = append(dirList, path)
			}
		}
		return nil
	})
	if err != nil {
		panic(err)
	}
	return dirList
}

// GetChildrenList 获取文件夹下的所有文件和文件夹
// 如果输入参数 relativePath 为 true, 则返回相对于根文件夹的相对路径, 否则返回绝对路径
func GetChildrenList(dirPath string, relativePath bool) []string {
	var childrenList []string
	err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
		// 排除 dirPath
		if path == dirPath {
			return nil
		}
		// 排除临时文件
		if IsTmpFileOrNotExists(path) {
			return nil
		}

		if relativePath {
			rel, _ := GetRelativePath(dirPath, path)
			childrenList = append(childrenList, rel)
		} else {
			childrenList = append(childrenList, path)
		}
		return nil
	})
	if err != nil {
		panic(err)
	}
	return childrenList
}

func CountChildren(dirPath string) (int, int, int, int) {
	var rootCount, dirCount, fileCount, totalCount int

	rootEntries, err := os.ReadDir(dirPath)
	if err != nil {
		panic(err)
	}
	rootCount = len(rootEntries)

	errWalk := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
		// 排除 dirPath
		if path == dirPath {
			return nil
		}
		// 排除临时文件
		if IsTmpFileOrNotExists(path) {
			return nil
		}

		if info.IsDir() {
			dirCount++
		} else {
			fileCount++
		}
		totalCount++
		return nil
	})
	if errWalk != nil {
		panic(err)
	}
	return rootCount, dirCount, fileCount, totalCount
}

// 获取相对于dirPath的相对路径
func GetRelativePathV1(path, dirPath string) string {
	return strings.Replace(path, dirPath, "", 1)
}

// 获取 path 相对于 dirPath 的相对路径
func GetRelativePath(dirPath, path string) (string, error) {
	rel, err := filepath.Rel(dirPath, path)
	if err != nil {
		return "", err
	}
	return rel, nil
}

func GetAbsolutePath(dirPath, path string) string {
	return filepath.Join(dirPath, path)
}

// 删除文件或整个文件夹
func DeleteFileOrDir(path string) error {
	return os.RemoveAll(path)
}

// GetFileHeaderHash
// 获取文件的头部Hash
func GetFileHeaderHash(path string, limit int64) (uint32, error) {
	file, err := os.Open(path)
	if err != nil {
		return 0, err
	}
	defer file.Close()

	buf := make([]byte, limit)
	n, _ := file.Read(buf)
	hash := crc32.NewIEEE()
	hash.Write(buf[:n])
	return hash.Sum32(), nil
}

func GetStdSizeInfo(size int64) string {
	kb := fmt.Sprintf("%d", size/1024)
	mb := fmt.Sprintf("%.1f", float64(size)/1024/1024)

	if size < 1024 { // 小于 1KB
		return fmt.Sprintf("%d bytes", size)
	} else if float64(size)/1024/1024 < 0.1 { // 小于 1OO KB / 0.1 MB
		return fmt.Sprintf("%s KB", kb)
	} else {
		return fmt.Sprintf("%s KB | %s MB", kb, mb)
	}

	//return fmt.Sprintf("%s bytes | %s KB | %s MB", size, kb, mb)
}

func GetFileSize(path string) int64 {
	file, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	fi, err := file.Stat()
	if err != nil {
		panic(err)
	}
	return fi.Size()
}

func GetFileSizeV2(path string) (int64, error) {
	file, err := os.Open(path)
	if err != nil {
		return -1, err
	}
	defer file.Close()

	fi, err := file.Stat()
	if err != nil {
		return -1, err
	}
	return fi.Size(), nil
}

func GetFileSizeV8(path string) (int64, error) {
	fi, err := os.Stat(path)
	if err != nil {
		return 0, err
	}
	return fi.Size(), nil
}

func GenerateRandomString() string {
	return rand.Text()
}

func CreateDirIfNotExists(path string) error {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return os.MkdirAll(path, os.ModePerm)
	}
	return nil
}

// 复制文件夹 src 到 dst
func CopyDir(src string, dst string) error {
	srcFs := os.DirFS(src)
	err := os.CopyFS(dst, srcFs)
	return err
}

func IsTmpFileOrNotExists(filePath string) bool {
	//if strings.HasSuffix(filePath, "~") || strings.HasPrefix(filePath, ".") {
	//if strings.HasSuffix(filePath, "~") {
	return (!CheckFileExists(filePath)) || strings.HasSuffix(filePath, "~")
}

func ReadDirAndIgnoreTmpFiles(dirPath string) []os.DirEntry {
	files, err := os.ReadDir(dirPath)
	if err != nil {
		panic(err)
	}
	var validFiles []os.DirEntry
	for _, file := range files {
		filePath := filepath.Join(dirPath, file.Name())
		if IsTmpFileOrNotExists(filePath) {
			log.Printf("⚠️ 跳过临时文件: %s", filePath)
			continue
		}
		validFiles = append(validFiles, file)
	}
	return validFiles
}

// GenerateUniqueFilePath 生成唯一的文件路径，通过在文件名后添加 _1, _2 等序号直到找到可用路径
func GenerateUniqueFilePath(filePath string) string {
	if !CheckFileExists(filePath) {
		return filePath
	}

	dir := filepath.Dir(filePath)
	base := filepath.Base(filePath)
	ext := filepath.Ext(base)
	name := strings.TrimSuffix(base, ext)

	counter := 1
	for {
		var newFileName string
		if ext != "" {
			newFileName = fmt.Sprintf("%s_%d%s", name, counter, ext)
		} else {
			newFileName = fmt.Sprintf("%s_%d", name, counter)
		}

		newFilePath := filepath.Join(dir, newFileName)
		if !CheckFileExists(newFilePath) {
			return newFilePath
		}

		counter++
	}
}

// CreateParentDirectories 创建给定文件路径的所有父级文件夹
func CreateParentDirectories(filePath string) error {
	// 获取文件的绝对路径
	absPath, err := filepath.Abs(filePath)
	if err != nil {
		return fmt.Errorf("无法获取绝对路径: %v", err)
	}

	// 获取父目录路径
	parentDir := filepath.Dir(absPath)

	// 如果父目录不存在，则递归创建所有必要的父级目录
	if err := os.MkdirAll(parentDir, os.ModePerm); err != nil {
		return fmt.Errorf("无法创建父级目录 %s: %v", parentDir, err)
	}

	//fmt.Printf("✅ 父级目录已创建: %s\n", parentDir)
	return nil
}

// ExpandWildcardsWithFilter 接收一个可能包含通配符的路径字符串，并返回所有匹配的文件路径列表。支持传入要过滤文件类型(例如: .png .jpg)
func ExpandWildcardsWithFilter(pattern string, filterFileTypes []string) ([]string, error) {
	// 如果模式中不包含通配符，则直接检查该路径是否存在。
	if !strings.ContainsAny(pattern, "*?") {
		return []string{pattern}, nil
	}

	// 创建一个空切片，用于存储匹配的文件路径。
	var files []string

	// 使用 filepath.Glob 来查找匹配的文件路径。
	matches, err := filepath.Glob(pattern)
	if err != nil {
		return nil, err
	}
	// 过滤文件
	for _, match := range matches {
		info, _ := os.Stat(match)
		if !info.IsDir() {
			files = append(files, match)
		}
	}

	// 过滤文件类型
	if files, err = FilterFileTypes(files, filterFileTypes); err != nil {
		return []string{}, err
	}

	return files, nil
}

// ExpandWildcards 接收一个可能包含通配符的路径字符串，并返回所有匹配的文件路径列表。
func ExpandWildcards(pattern string) ([]string, error) {
	return ExpandWildcardsWithFilter(pattern, nil)
}

// FilterFileTypes 过滤 []string 文件类型
func FilterFileTypes(paths []string, filterFileTypes []string) ([]string, error) {
	if filterFileTypes != nil && len(filterFileTypes) > 0 {
		var filtered []string
		for _, path := range paths {
			fileInfo, err := os.Stat(path)
			if err != nil {
				return nil, err
			}
			if fileInfo.IsDir() {
				continue
			}
			for _, fileType := range filterFileTypes {
				// 让 fileType 兼容 .png png 这样的写法
				if strings.HasPrefix(fileType, ".") {
					fileType = fileType[1:]
				}

				if strings.HasSuffix(path, fileType) {
					filtered = append(filtered, path)
					break
				}
			}
		}
		paths = filtered
	}
	return paths, nil
}

func FilterFileTypesV1(paths []string, filterFileTypes []string) ([]string, error) {
	if filterFileTypes != nil && len(filterFileTypes) > 0 {
		var filtered []string
		for _, path := range paths {
			ext := filepath.Ext(path)
			ext = strings.ToLower(ext)

			match := false
			for _, ft := range filterFileTypes {
				ft = strings.ToLower(ft)
				if strings.HasPrefix(ft, ".") && ext == ft {
					match = true
					break
				} else if "."+ext == ft {
					match = true
					break
				}
			}
			if match {
				filtered = append(filtered, path)
			}
		}
		paths = filtered
	}
	return paths, nil
}
